#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>
#include <errno.h>
#include <fstream>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>

#include "Socket.hpp"
#include "log.hpp"

using namespace std;

const string wwwroot = "./wwwroot";
const string sep = "\r\n";
const string homepage = "index.html";

// 封装ThreadData
class ThreadData
{
public:
    ThreadData(int sockfd) : _sockfd(sockfd)
    {
    }
    ~ThreadData()
    {
    }

public:
    int _sockfd;
};

class Request
{
public:
    void Deserialize(string req)
    {
        int pos = 0;
        // 切割字符串
        while(true)
        {
            pos = req.find(sep); // 通过\r\n分隔符进行查找
            if(pos == string::npos) break; // 这里如果没找到就证明已到达字符串末尾，没有更多分隔符
            string tmp = req.substr(0,pos); // 找到后就进行字符串截取
            if(tmp.empty()) break; // 截取为空证明遇到了连续的\r\n分隔符，即报头部分已结束（空行是报头与正文的分隔标志）
            _req_header.push_back(tmp); // 当截取后不为空就push到报头部分
            req.erase(0,pos+sep.size()); // 将查找后并push进报头部分的字符串进行删除，还要删除分割字符串
        }
        // 切割完报头部分后，剩下的都是正文部分
        _text = req;
        // DebugPrint(); // Debug进行打印观察
    }

    // 解析字符串
    void parse()
    {
        // 字符串流（stringstream）的格式化提取特性
        stringstream ss(_req_header[0]);
        ss>>_method>>_url>>_http_version;

        // 处理路径问题
        _path = wwwroot; // "./wwwroot"
        if(_url == "/" || _url == "/index.html")
        {
            _path += "/";
            _path += homepage;
        }
        else 
        {
            _path += _url;
        }
    }

    void DebugPrint()
    {
        cout<<"--------------------------------------------------------------------"<<endl;
        for(auto& ch : _req_header)
        {
            cout<<ch<<endl<<endl;
        }

        cout<<_text<<endl;
    }

public:
    vector<string> _req_header;
    string _text;

    // 请求行三个：方法，URL，版本
    string _method;
    string _url;
    string _http_version;

    // URL路径
    string _path;
};

class HttpServer
{
public:
    // 构造析构
    HttpServer(uint16_t port) : _port(port)
    {
    }
    // Start接口
    bool Start()
    {
        // 创建套接字，绑定，监听
        _listenfd.Socket();
        _listenfd.Bind(_port);
        _listenfd.Listen();
        // 循环
        while (true)
        {
            // 利用多线程进行调用
            // 这里是服务端，所以就需要先接收客户端的IP和port
            string clientip;
            uint16_t clientport;
            int sockfd = _listenfd.Accept(&clientip, &clientport);
            if (sockfd < 0)
            {
                lg(FATAL, "accept error,error:%d errorstring:%s", errno, strerror(errno));
                continue;
            }
            lg(INFO, "get a new link sockfd = %d", sockfd);
            // 接着创建线程（四个参数）
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

    static string ReadHtmlContent(const string& path)
    {
        // 文件打开
        ifstream in(path);
        // 文件打开检查
        if(!in.is_open()) return "404";
        // 读取文件内容
        string content;
        string line;
        while(getline(in,line))
        {
            content += line;
        }
        // 关闭文件并返回结果
        in.close();
        return content;    
    }   

    static void HanderHttp(int sockfd)
    {
        // 从sockfd套接字中读取数据，recv
        char buffer[10240];
        ssize_t n = recv(sockfd,buffer,sizeof(buffer)-1,0);
        // 如果读取成功将读取得到的数据打印出来，然后返回响应过程，得到响应后通过send发送给请求
        if(n > 0)
        {
            buffer[n] = 0; // 把读上来的数据当做字符串
            cout<<buffer;

            // 构建请求类
            Request rq;
            rq.Deserialize(buffer);
            rq.parse();

            // 在返回响应过程中，其中的主体是需要进行网页文件的，那么读取网页文件封装一个函数
            //正文内容
            string text = ReadHtmlContent(rq._path);
            //状态行
            string response_line = "HTTP/1.1 200 OK\r\n";
            //报头
            string response_header = "Content-Length:";
            response_header += to_string(text.size());
            response_header += "\r\n";
            //空行
            string response_blank = "\r\n";
            
            //融合请求
            string response;
            response += response_line;
            response += response_header;
            response += response_blank;
            response += text;
            //发送
            send(sockfd,response.c_str(),response.size(),0);
        }
        close(sockfd);
    }

    static void *ThreadRun(void *args)
    {
        // 线程分离
        pthread_detach(pthread_self());
        // 拿到创建线程中所传的参数
        ThreadData *td = static_cast<ThreadData *>(args);
        // 调用HanderHttp方法
        HanderHttp(td->_sockfd);
        // 后续工作
        delete td;
        return nullptr;
    }
    ~HttpServer()
    {
    }

private:
    // 监听套接字和服务端的端口号
    Sock _listenfd;
    // 端口号从命令行中一步步传参过来
    uint16_t _port;
};